package framework.automaton;

import java.util.ArrayList;

import framework.grid.Grid;
import framework.grid.Location;

/**
 * This concrete class puts everything together and runs the automata
 *  
 * @author Bill Ge, Alex Yoo, May Zhai
 */
public class Updater {
	
	/**
	 * An ArrayList of Grids that stores all the previous grids up to the
	 * current Grid.
	 * 
	 * <br>Array Index 0 references the current Grid.
	 * <br>Later indices references past Grids, with 1 being the previous Grid,
	 * 2 being the next previous Grid, and so on. 
	 */
	protected ArrayList<Grid> history;
	
	/** Tracker to determine which turn number the application is currently
	 *  focused upon. */
	private int currentTurn;
	
	/** Maximum of how many grids can be kept within the history. */
	private static int recordLimit = 50;
	
	/** Counter to keep track of how many turns have passed. */
	private int turnCounter = 0;
	
	/** Gives the game access to the Logic used to run the game. */
	protected Logic logic;
	
	/** Constructor that allows the user to set the desired recording limit,
	 *  grid, and Logic parameters. */
	public Updater(Logic logic, Grid grid, int limit){
		this(logic,grid);
		this.recordLimit = limit;
	}
	
	/** Constructor works to initialize the global variables, but not directly
	 *  changing the recording limit. */
	public Updater(Logic logic, Grid grid){
		this.logic = logic;
		history = new ArrayList<Grid>();
		currentTurn = 0;
		setGrid(grid);
	}
	
	/**
	 * Mutator method, adds this grid to the history and set it as most recent.
	 * @param grid
	 */
	public void setGrid(Grid grid){
		history.add(0,grid);
	}
	
	/**
	 * Accessor method for the current grid.
	 * @return
	 */
	public Grid getGrid(){
		return history.get(currentTurn);
	}
	
	/**
	 * Accessor method returns the grid associated with the given turn number.
	 * It returns null if the turn number is invalid or history is empty.  
	 * @param turnNum
	 * @return
	 */
	public Grid getTurnNumber(int turnNum){
		if(turnNum < 0 || turnNum >= recordLimit || history==null){
			return null;
		}
		return history.get(turnNum);
	}
	
	/**
	 * For the custom feature. Rewinds one turn, stepping currentTurn one
	 * step back and returning the corresponding Grid.
	 * 
	 * @return the Grid one step before the current Grid.
	 */
	public Grid getPreviousTurn(){
		if(history!=null && currentTurn+1<history.size()){
			currentTurn++;
			return history.get(currentTurn);
		}
		return history.get(history.size()-1);
	}
	
	/**
	 * Advances one turn from the most recent Grid and returns that Grid.
	 * 
	 * @return the Grid one step after the most recent Grid.
	 */
	private Grid advanceTurn(){
		Grid currentGrid = history.get(0);
		int rows = currentGrid.getNumRows();
		int cols = currentGrid.getNumCols();
		
		Grid newGrid = history.get(0).createNewGrid(rows,cols); 
		if(currentGrid == null){
			return null;
		}

		// Iterate through every spot in the Grid and calculate what needs to
		// be there, according to the Logic object.
		for(int i = 0; i < rows;i++){
			for(int j=0; j < cols; j++){
				Location temp = new Location(i,j);
				newGrid.put(temp, logic.advanceTurn(currentGrid, temp));
			}
		}
		turnCounter++;
		history.add(0, newGrid);
		
		// If the history is full according to recordLimit, then boot out
		// the oldest Grid
		if(history.size() == recordLimit)
			history.remove(recordLimit-1);

		return newGrid;
	}
	
	/**
	 * For the custom feature. Advances one turn from where the current Grid is
	 * pointed at (not necessarily the most recent), stepping currentTurn one
	 * step forward and returning the corresponding Grid. If Updater is already
	 * at the most recent turn, then call advanceTurn().
	 * 
	 * @return the Grid one step after the Grid that currentTurn is pointing at
	 */
	public Grid getNextTurn(){
		if(history == null){
			return null;
		}
		
		// Call advanceturn if the Updater is at the most recent Grid;
		// otherwise decrement currentTurn and grab the next Grid from history.
		if(currentTurn == 0){
			return advanceTurn();
		}else{
			if(currentTurn-1 >= 0){
				currentTurn--;
				return history.get(currentTurn);	
			}
		}
		return null;
	}
}
